package com.csii.activiti.server.core.service.impl;

import cn.hutool.core.collection.CollUtil;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.csii.activiti.server.abstraction.constant.MessageType;
import com.csii.activiti.server.abstraction.entity.ActCallbackMessage;
import com.csii.activiti.server.abstraction.entity.ActFlowCallbackResult;
import com.csii.activiti.server.abstraction.entity.ActFlowRemoteCallback;
import com.csii.activiti.server.abstraction.entity.ActSendMessage;
import com.csii.activiti.server.abstraction.service.ActBasicService;
import com.csii.activiti.server.abstraction.service.optional.ActCallbackService;
import com.csii.activiti.server.core.entity.db.ActFlowCallbackEntity;
import com.csii.activiti.server.core.entity.db.ActFlowConfigurationEntity;
import com.csii.activiti.server.core.listener.base.BaseLocalCallbackListener;
import com.csii.activiti.server.core.mapper.ActFlowCallbackMapper;
import com.csii.activiti.server.core.mapper.ActFlowConfigurationMapper;
import com.csii.activiti.server.core.query.QueryFlowCallback;
import com.csii.activiti.server.core.service.FlowCallbackManagementService;
import com.csii.activiti.server.core.utils.IdUtils;
import com.csii.activiti.server.sign.encrypt.utils.SignEncryptUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Service
@Slf4j
public class FlowCallbackManagementServiceImpl implements FlowCallbackManagementService {

    @Autowired
    private ActFlowCallbackMapper actFlowCallbackMapper;

    @Autowired
    private ActCallbackService actCallbackService;

    @Autowired
    private ActFlowConfigurationMapper actFlowConfigurationMapper;

    @Value("${activity.callback.open:true}")
    private boolean openCallback;

    @Value("${activity.callback.retriesCount:5}")
    private int retriesCount;

    @Autowired
    private ActBasicService actBasicService;

    private Map<String, BaseLocalCallbackListener> localCallback;

    @Autowired(required = false)
    private void initLocalCallback(List<BaseLocalCallbackListener> listenerList){
        localCallback = listenerList.stream().filter(item -> item.listenerFlowKey() != null).collect(Collectors.toMap(BaseLocalCallbackListener::listenerFlowKey, item -> item, (v1, v2) -> v2));
    }


    @Override
    @Async
    public void callback(ActSendMessage sendMessage, String callbackType) {
        List<ActFlowRemoteCallback> callbackList = sendMessage.getCallbackList();
        ActFlowConfigurationEntity flowConfigurationEntity = actFlowConfigurationMapper.selectOne(new LambdaQueryWrapper<ActFlowConfigurationEntity>().select(ActFlowConfigurationEntity::getFlowSignKey).eq(ActFlowConfigurationEntity::getFlowKey, sendMessage.getProcessKey()));
        String flowSignKey = null;
        if (flowConfigurationEntity != null){
            flowSignKey = flowConfigurationEntity.getFlowSignKey();
        }
        //本地回调
        if (localCallback != null && localCallback.containsKey(sendMessage.getProcessKey())) {
            try {
                localCallback.get(sendMessage.getProcessKey()).callback(new ActCallbackMessage(sendMessage),MessageType.byType(callbackType));
            }catch (Exception e){
                log.error("localCallback is error flowKey = "+sendMessage.getProcessKey() +"error : {}",e);
            }
        }
        //远程回调
        if (openCallback && CollUtil.isNotEmpty(callbackList)){
            for (ActFlowRemoteCallback actFlowRemoteCallback : callbackList) {
                if (callbackType.equals(actFlowRemoteCallback.getCallbackType())){
                    sendMessage.setCallbackList(CollUtil.newArrayList(actFlowRemoteCallback));
                    ActCallbackMessage actCallbackMessage = new ActCallbackMessage(sendMessage);
                    if (flowSignKey != null){
                        actCallbackMessage.setCallbackSign(SignEncryptUtils.encrypt(actCallbackMessage, flowSignKey));
                    }
                    ActFlowCallbackEntity actFlowCallbackEntity = new ActFlowCallbackEntity(actCallbackMessage, callbackType, IdUtils.getId(),actFlowRemoteCallback.getCallbackUrl());
                    actFlowCallbackMapper.insert(actFlowCallbackEntity);
                    //回调
                    callback(actFlowCallbackEntity,actCallbackMessage,actFlowRemoteCallback.getCallbackUrl());
                }
            }
        }
    }


    private void callback(ActFlowCallbackEntity actFlowCallbackEntity,ActCallbackMessage callbackMessage,String callbackUrl){
        try {
            ActFlowCallbackResult callbackResult = actCallbackService.callback(callbackMessage, callbackUrl);
            if (callbackResult.isResult()){
                actFlowCallbackMapper.deleteById(actFlowCallbackEntity.getId());
            }else{
                ActFlowCallbackEntity updateFlowCallbackEntity = new ActFlowCallbackEntity();
                updateFlowCallbackEntity.setErrorMessage(callbackResult.getMessage());
                updateFlowCallbackEntity.setId(actFlowCallbackEntity.getId());
                if (actFlowCallbackEntity.getCallbackStatus() == 0){
                    updateFlowCallbackEntity.setCallbackStatus(1);
                }else{
                    updateFlowCallbackEntity.setRetriesCount(actFlowCallbackEntity.getRetriesCount()+1);
                }
                actFlowCallbackMapper.updateById(updateFlowCallbackEntity);
            }
        }catch (Exception e){
            ActFlowCallbackEntity updateFlowCallbackEntity = new ActFlowCallbackEntity();
            updateFlowCallbackEntity.setErrorMessage(e.toString());
            updateFlowCallbackEntity.setId(actFlowCallbackEntity.getId());
            if (actFlowCallbackEntity.getCallbackStatus() == 0){
                updateFlowCallbackEntity.setCallbackStatus(1);
            }else{
                updateFlowCallbackEntity.setRetriesCount(actFlowCallbackEntity.getRetriesCount()+1);
            }
            actFlowCallbackMapper.updateById(updateFlowCallbackEntity);
        }
    }

    @Override
    public void reCallbackById(String id) {
        ActFlowCallbackEntity actFlowCallbackEntity = actFlowCallbackMapper.selectById(id);
        ActCallbackMessage callbackMessage = JSONObject.parseObject(actFlowCallbackEntity.getJsonData(), ActCallbackMessage.class);
        //回调
        callback(actFlowCallbackEntity,callbackMessage,actFlowCallbackEntity.getCallbackUrl());

    }

    @Override
    public void updateFlowCallbackData(ActFlowCallbackEntity actFlowCallbackEntity) {
        actFlowCallbackMapper.updateById(actFlowCallbackEntity);
    }

    @Override
    public List<ActFlowCallbackEntity> queryCallbackEntityLtRetriesCount(int retriesCount) {
        return actFlowCallbackMapper.selectList(new LambdaQueryWrapper<ActFlowCallbackEntity>().select(ActFlowCallbackEntity::getId).lt(ActFlowCallbackEntity::getRetriesCount,retriesCount));
    }

    @Override
    public Page<ActFlowCallbackEntity> queryData(QueryFlowCallback queryFlowCallback, Page page) {
        String currentTenantId = actBasicService.getCurrentTenantId();
        Page<ActFlowCallbackEntity> resultPage = actFlowCallbackMapper.queryData(page,queryFlowCallback, retriesCount,currentTenantId);
        return resultPage;
    }

}
